#include "stdafx.h"
#include "Server.h"
	
enum GameMessages
{
	    SPAWN_POSITION=ID_USER_PACKET_ENUM+1,
	    POSITION_UPDATE=ID_USER_PACKET_ENUM+2,
	    NEW_CLIENT=ID_USER_PACKET_ENUM+3,
		PLAYER_DEATH=ID_USER_PACKET_ENUM+4,
		PLAYER_DISCONNECT=ID_USER_PACKET_ENUM+5
};

void Server::createServer(char* port, char* password, char* maxNumPlayers) {
	maxPlayers = maxNumPlayers;
	serverPort = port;
	serverPassword = password;

	server = RakNet::RakPeerInterface::GetInstance();
	server -> SetIncomingPassword(password, (int) strlen(password));
	server -> SetTimeoutTime(1000, RakNet::UNASSIGNED_SYSTEM_ADDRESS);

	// Can not create server
	if(!server) {
		std::cout << "Failed to create server." << std::endl;
	} else {
		std::cout << "Server created!" << std::endl;
	}

	bool b = server->Startup(atoi(maxPlayers), &RakNet::SocketDescriptor(atoi(serverPort),0), 1) == RakNet::RAKNET_STARTED;
	if(!b) {
		std::cout << "Failed to start server." << std::endl;
	}

	server -> SetMaximumIncomingConnections(atoi(maxPlayers));
	server -> SetOccasionalPing(true);
	server -> SetUnreliableTimeout(2000);
}

void Server::serverInfo() {
	DataStructures::List<RakNet::RakNetSmartPtr<RakNet::RakNetSocket>> sockets;
	server -> GetSockets(sockets);
	
	std::cout << "Socket adresses: " << std::endl;

	for(unsigned int i = 0; i < sockets.Size(); i++) {
		std::cout << "#: " << i << ": " << sockets[i]->boundAddress.ToString(true) << std::endl;
	}
}

void Server::receivePackets() {
	for (packet=server->Receive(); packet; server->DeallocatePacket(packet), packet=server->Receive()) {
	    
		RakNet::BitStream bsIn(packet->data,packet->length,false);
		bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
		//bsIn.Read(rs);
	
		std::cout << "Client List:\n";
		for(int i=0; i < (int)clients.size(); ++i) {
			std::cout << i << " - " << clients[i].guid.g << std::endl;
		}

		std::cout << "\n\nNew Packet from:" << packet->guid.g << std::endl;
	
		int client_id=0;
		int x=0,y=0,z=0;

		switch (packet->data[0]) {
			case ID_REMOTE_DISCONNECTION_NOTIFICATION:
				printf("Another client has disconnected.\n");
				break;
			case ID_REMOTE_CONNECTION_LOST:
				printf("Another client has lost the connection.\n");
				break;
			case ID_REMOTE_NEW_INCOMING_CONNECTION:
				printf("Another client has connected.\n");
				break;
			case ID_NEW_INCOMING_CONNECTION:
				printf("A connection is incoming.\n");
	
				client_id = (int)clients.size();
	
				if((int)clients.size() > 0) {
					// send new client notification to existing clients
					std::cout << "Sending new spawn position to each client\n";
					bsOut.Reset();
					bsOut.Write((RakNet::MessageID)NEW_CLIENT);
					bsOut.Write(client_id);
					bsOut.Write(20);
					bsOut.Write(20);
					bsOut.Write(10);

					for(int i=0; i < (int)clients.size(); ++i) {
						std::cout << "  To: " << i << " - " << clients[i].guid.g << std::endl;
						server->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,
						server->GetSystemAddressFromGuid(clients[i].guid),false);
					}
					bsOut.Reset();
	
					std::cout << "Sending each client's position to new client\n";
					for (int i = 0; i < (int)clients.size(); ++i) {
						std::cout << "sending for " << i << std::endl;
						bsOut.Reset();
						bsOut.Write((RakNet::MessageID)NEW_CLIENT);
						bsOut.Write(i);
						bsOut.Write(clients[i].x);
						bsOut.Write(clients[i].y);
						bsOut.Write(clients[i].z);
						server->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,
							packet->systemAddress,false);
					}
				}
	
				// Add client
				clients.push_back(Clients(client_id));
				clients[client_id].guid = packet->guid;
				clients[client_id].x = 20;
				clients[client_id].y = 20;
				clients[client_id].z = 10;
				// Use a BitStream to write a custom user message
				// Bitstreams are easier to use than sending casted structures, and handle endian swapping automatically
				bsOut.Reset();
				bsOut.Write((RakNet::MessageID)SPAWN_POSITION);
				bsOut.Write(client_id);
				bsOut.Write(20);
				bsOut.Write(20);
				bsOut.Write(10);
				server->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
				bsOut.Reset();
				/*
				bsOut.Write((MessageID)YOUR_TURN);
				server->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
				*/
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				printf("A client has disconnected.\n");
				break;
			case ID_CONNECTION_LOST:
				printf("A client lost the connection.\n");
				break;
			case POSITION_UPDATE:
				// received new position from client
				bsIn.Read(client_id);
				bsIn.Read(x);
				bsIn.Read(y);
				bsIn.Read(z);
				printf("Client %d sent new position %d,%d,%d\n", client_id, x,y,z);
				// save to vectors
				clients[client_id].x = x;
				clients[client_id].y = y;
				clients[client_id].z = z;
				std::cout << "Sending new position value to each client\n";
				bsOut.Reset();
				bsOut.Write((RakNet::MessageID)POSITION_UPDATE);
				bsOut.Write(client_id);
				bsOut.Write(x);
				bsOut.Write(y);
				bsOut.Write(z);
				for(int i=0; i < (int)clients.size(); ++i) {
					if(client_id != i)
					{
						std::cout << "  To: " << i << " - " << clients[i].guid.g << std::endl;
						server->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,
							server->GetSystemAddressFromGuid(clients[i].guid),false);
					}
					else {
						std::cout << "  Not sending to own client: " << client_id << "\n";
					}
				}
				bsOut.Reset();
				break;
			default:
				printf("Message with identifier %i has arrived.\n", packet->data[0]);
				break;
		}
		bsOut.Reset();
	}
}

void Server::terminateServer() {
	RakNet::RakPeerInterface::DestroyInstance(server);
}